//
//  WebHandler.cpp
//  Phidgets
//
//  Created by Daniel Hazelbaker on 12/28/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#include "WebHandler.h"
#include "WebServer.h"
#include "json.h"
#include "ScriptData.h"
#include "ScriptDataCollection.h"


static void GetInfo(WebHandlerData &data, const StringDictionary &parameters);
static void PutJsonScript(WebHandlerData &data, const StringDictionary &parameters);
static void GetJsonScriptById(WebHandlerData &data, const StringDictionary &parameters);
static void PutJsonScriptById(WebHandlerData &data, const StringDictionary &parameters);
static void DeleteJsonScriptById(WebHandlerData &data, const StringDictionary &parameters);
static void GetJsonScriptList(WebHandlerData &data, const StringDictionary &parameters);
static void PostJsonScriptVerify(WebHandlerData &data, const StringDictionary &parameters);


//
// Register all the web handlers. Later on I need to find a way to split this up as there will
// be a lot of handlers.
//
void RegisterWebHandlers(WebServer &server)
{
    server.RegisterHandler("/info", "GET", GetInfo);
    server.RegisterHandler("/json/script", "PUT", PutJsonScript);
    server.RegisterHandler("/json/script/{id}", "GET", GetJsonScriptById);
    server.RegisterHandler("/json/script/{id}", "PUT", PutJsonScriptById);
    server.RegisterHandler("/json/script/{id}", "DELETE", DeleteJsonScriptById);
    server.RegisterHandler("/json/script/list", "GET", GetJsonScriptList);
    server.RegisterHandler("/json/script/verify", "POST", PostJsonScriptVerify);
}



//
// Handle a call to /info
//
static void GetInfo(WebHandlerData &data, const StringDictionary &parameters)
{
    data.response.addContent("Info called");
}


//
// Handle a GET request to /json/script/{id}
//
static void GetJsonScriptById(WebHandlerData &data, const StringDictionary &parameters)
{
    Json::Value json;
    Json::StyledWriter writer;
    ScriptData script = ScriptData(atoi(parameters.at("id").c_str()));


    if (script.GetScriptID() == -1)
    {
        data.response.setStatus(404);
        data.response.setContentType("text/plain");
        data.response.addContent("Invalid script ID specified.");
    }
    else {
        json["id"] = script.GetScriptID();
        json["name"] = script.GetName();
        json["type"] = script.GetRunType();
        json["code"] = script.GetCode();
        
        data.response.setContentType("application/json");
        data.response.addContent(writer.write(json));
    }
}


//
// Handle a PUT request to /json/script/{id}. Data should be a Json object with the following
// keys:
//  name - Name of script
//  runtype - Type of script (daemon, run-once, etc.)
//  code - The JS code for the script
//
static void PutJsonScriptById(WebHandlerData &data, const StringDictionary &parameters)
{
    ScriptData script = ScriptData(atoi(parameters.at("id").c_str()));
    const void *buf;
    size_t len = 0;
    Json::StyledWriter writer;
    Json::Reader reader;
    Json::Value json, input;
    
    
    //
    // Parse the input data.
    //
    buf = data.request.getPostData(&len);
    reader.parse(std::string((const char *)buf, len), input);
    
    //
    // Verify we have a valid script ID.
    //
    if (script.GetScriptID() == -1)
    {
        data.response.setStatus(404);
        data.response.setContentType("text/plain");
        data.response.addContent("Invalid script ID specified.");
    }

    //
    // Set values.
    //
    try {
        if (input.isMember("name") == true)
            script.SetName(input["name"].asString());
        if (input.isMember("runtype") == true)
        {
            if (input["runtype"].type() == Json::stringValue)
                script.SetRunType((ScriptRunType)atoi(input["runtype"].asString().c_str()));
            else
                script.SetRunType((ScriptRunType)input["runtype"].asInt());
        }
        if (input.isMember("code") == true)
            script.SetCode(input["code"].asString());
    }
    catch (...) {
        //
        // User supplied invalid data.
        //
        data.response.setStatus(400);
        data.response.setContentType("text/plain");
        data.response.addContent("Invalid request format.");

        return;
    }

    script.Save();
    
    json["id"] = (int)script.GetScriptID();
        
    data.response.setContentType("application/json");
    data.response.addContent(writer.write(json));
}


//
// Handle a DELETE request to /json/script/{id}. No data is accepted.
//
static void DeleteJsonScriptById(WebHandlerData &data, const StringDictionary &parameters)
{
    ScriptData script = ScriptData(atoi(parameters.at("id").c_str()));
    
    
    //
    // Verify we have valid information.
    //
    if (script.GetScriptID() == -1)
    {
        data.response.setStatus(404);
        data.response.setContentType("text/plain");
        data.response.addContent("Invalid script ID specified.");
    }
    
    //
    // Perform the delete.
    //
    if (script.Delete() == true)
    {
        data.response.setStatus(200);
    }
    else
    {
        data.response.setStatus(500);
        data.response.setContentType("text/plain");
        data.response.addContent("Unknown error trying to delete.");
    }
}


//
// Handle a PUT request to /json/script. Data should be a Json object with the following
// keys:
//  name - Name of script
//  runtype - Type of script (daemon, run-once, etc.)
//  code - The JS code for the script
//
static void PutJsonScript(WebHandlerData &data, const StringDictionary &parameters)
{
    ScriptData script = ScriptData();
    const void *buf;
    size_t len = 0;
    Json::StyledWriter writer;
    Json::Reader reader;
    Json::Value json, input;
    
    
    //
    // Parse the input data.
    //
    buf = data.request.getPostData(&len);
    reader.parse(std::string((const char *)buf, len), input);

    //
    // Verify we have valid information.
    //
    if (input.isMember("name") == false || input.isMember("runtype") == false || input.isMember("code") == false)
    {
        data.response.setStatus(400);
        data.response.setContentType("text/plain");
        data.response.addContent("Invalid information supplied with request.");
    }
    else
    {
        //
        // Set values.
        //
        try {
            if (input.isMember("name") == true)
                script.SetName(input["name"].asString());
            if (input.isMember("runtype") == true)
            {
                if (input["runtype"].type() == Json::stringValue)
                    script.SetRunType((ScriptRunType)atoi(input["runtype"].asString().c_str()));
                else
                    script.SetRunType((ScriptRunType)input["runtype"].asInt());
            }
            if (input.isMember("code") == true)
                script.SetCode(input["code"].asString());
        }
        catch (...) {
            //
            // User supplied invalid data.
            //
            data.response.setStatus(400);
            data.response.setContentType("text/plain");
            data.response.addContent("Invalid request format.");
            
            return;
        }
        
        script.Save();
        
        json["id"] = (int)script.GetScriptID();
        
        data.response.setStatus(201);
        data.response.setContentType("application/json");
        data.response.addContent(writer.write(json));
    }
}


//
// Handle a call to /json/script/list
//
static void GetJsonScriptList(WebHandlerData &data, const StringDictionary &parameters)
{
    ScriptDataCollection sdc;
    ScriptDataCollection::iterator sdcIt;
    Json::Value json, jsonScript;
    Json::StyledWriter writer;
    

    sdc.LoadAll();
    for (sdcIt = sdc.begin(); sdcIt != sdc.end(); sdcIt++)
    {
        jsonScript["id"] = (*sdcIt).GetScriptID();
        jsonScript["name"] = (*sdcIt).GetName();
        jsonScript["runtype"] = (*sdcIt).GetRunType();
        jsonScript["size"] = (int)(*sdcIt).GetCode().size();

        json["scripts"].append(jsonScript);
    }
    
    data.response.setContentType("application/json");
    data.response.addContent(writer.write(json));
}


//
// Handle a call to /json/script/verify
//
static void PostJsonScriptVerify(WebHandlerData &data, const StringDictionary &parameters)
{
    Json::Value json, j_error;
    Json::StyledWriter writer;
    MonkeyContext *ctx;
    std::list<MonkeyError> errors;
    std::list<MonkeyError>::iterator eit;
    
    
    ctx = CreateStandardMonkeyContext();
    
    std::string script = std::string((const char *)data.request.getPostData(NULL));
    
    json["status"] = ctx->ValidateScript(script.c_str());
    
    errors = ctx->GetErrors();
    json["errors"] = Json::Value(Json::arrayValue);
    for (eit = errors.begin(); eit != errors.end(); eit++)
    {
        j_error = Json::Value(Json::objectValue);
        j_error["lineno"] = (*eit).lineNumber();
        j_error["code"] = (*eit).code();
        j_error["error"] = (*eit).isError();
        j_error["message"] = (*eit).message();
        
        json["errors"].append(j_error);
    }
    
    data.response.setContentType("application/json");
    data.response.addContent(writer.write(json));
}


